<!-- 升级版Markdown编辑器组件 - 支持更多格式和文件上传功能 -->
<template>
    <div class="markdown-editor-enhanced">
        <!-- 编辑器工具栏 -->
        <div class="editor-toolbar">
            <!-- 基础格式工具栏 -->
            <div class="toolbar-section">
                <el-button-group>
                    <!-- 标题按钮组 -->
                    <el-dropdown @command="insertHeading" trigger="click">
                        <el-button size="small" title="标题">
                            <el-icon>
                                <Edit />
                            </el-icon>
                            <el-icon class="el-icon--right">
                                <ArrowDown />
                            </el-icon>
                        </el-button>
                        <template #dropdown>
                            <el-dropdown-menu>
                                <el-dropdown-item command="h1">一级标题</el-dropdown-item>
                                <el-dropdown-item command="h2">二级标题</el-dropdown-item>
                                <el-dropdown-item command="h3">三级标题</el-dropdown-item>
                                <el-dropdown-item command="h4">四级标题</el-dropdown-item>
                                <el-dropdown-item command="h5">五级标题</el-dropdown-item>
                                <el-dropdown-item command="h6">六级标题</el-dropdown-item>
                            </el-dropdown-menu>
                        </template>
                    </el-dropdown>

                    <!-- 文本格式按钮 -->
                    <el-button size="small" @click="insertMarkdown('**', '**')" title="粗体">
                        <el-icon>
                            <Document />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('*', '*')" title="斜体">
                        <el-icon>
                            <ChatDotRound />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('~~', '~~')" title="删除线">
                        <el-icon>
                            <Close />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('`', '`')" title="行内代码">
                        <el-icon>
                            <DocumentCopy />
                        </el-icon>
                    </el-button>
                </el-button-group>
            </div>

            <!-- 列表工具栏 -->
            <div class="toolbar-section">
                <el-button-group>
                    <el-button size="small" @click="insertMarkdown('- ', '')" title="无序列表">
                        <el-icon>
                            <List />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('1. ', '')" title="有序列表">
                        <el-icon>
                            <View />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('- [ ] ', '')" title="任务列表">
                        <el-icon>
                            <Check />
                        </el-icon>
                    </el-button>
                </el-button-group>
            </div>

            <!-- 链接和媒体工具栏 -->
            <div class="toolbar-section">
                <el-button-group>
                    <el-button size="small" @click="insertLink" title="链接">
                        <el-icon>
                            <Link />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertImage" title="图片">
                        <el-icon>
                            <Picture />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertTable" title="表格">
                        <el-icon>
                            <Grid />
                        </el-icon>
                    </el-button>
                </el-button-group>
            </div>

            <!-- 代码和引用工具栏 -->
            <div class="toolbar-section">
                <el-button-group>
                    <el-button size="small" @click="insertCodeBlock" title="代码块">
                        <el-icon>
                            <DocumentCopy />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('> ', '')" title="引用">
                        <el-icon>
                            <ChatDotRound />
                        </el-icon>
                    </el-button>
                    <el-button size="small" @click="insertMarkdown('---\n', '')" title="分割线">
                        <el-icon>
                            <Minus />
                        </el-icon>
                    </el-button>
                </el-button-group>
            </div>

            <!-- 文件上传工具栏 -->
            <div class="toolbar-section">
                <el-upload ref="fileUploadRef" :show-file-list="false" :before-upload="beforeFileUpload"
                    :http-request="handleFileUpload" accept=".md,.markdown" class="file-upload-btn">
                    <el-button size="small" title="上传MD文件">
                        <el-icon>
                            <Upload />
                        </el-icon>
                    </el-button>
                </el-upload>
            </div>

            <!-- 编辑器模式切换 -->
            <div class="toolbar-section">
                <el-radio-group v-model="editorMode" size="small">
                    <el-radio-button label="edit">编辑</el-radio-button>
                    <el-radio-button label="preview">预览</el-radio-button>
                    <el-radio-button label="split">分屏</el-radio-button>
                </el-radio-group>
            </div>
        </div>

        <!-- 编辑器内容区域 -->
        <div class="editor-content" :class="editorMode" v-drag-upload="dragUploadOptions">
            <!-- 编辑模式 -->
            <div v-if="editorMode === 'edit'" class="edit-panel">
                <el-input v-model="content" type="textarea" :placeholder="placeholder" :rows="20"
                    class="markdown-textarea" @input="handleContentChange" />
            </div>

            <!-- 预览模式 -->
            <div v-else-if="editorMode === 'preview'" class="preview-panel">
                <div class="preview-content" v-html="renderedContent"></div>
            </div>

            <!-- 分屏模式 -->
            <div v-else-if="editorMode === 'split'" class="split-panel">
                <div class="edit-panel">
                    <el-input v-model="content" type="textarea" :placeholder="placeholder" :rows="20"
                        class="markdown-textarea" @input="handleContentChange" />
                </div>
                <div class="preview-panel">
                    <div class="preview-content" v-html="renderedContent"></div>
                </div>
            </div>
        </div>

        <!-- 链接插入对话框 -->
        <el-dialog v-model="linkDialogVisible" title="插入链接" width="400px">
            <el-form :model="linkForm" label-width="80px">
                <el-form-item label="链接文本">
                    <el-input v-model="linkForm.text" placeholder="请输入链接文本" />
                </el-form-item>
                <el-form-item label="链接地址">
                    <el-input v-model="linkForm.url" placeholder="请输入链接地址" />
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="linkDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="confirmInsertLink">确定</el-button>
            </template>
        </el-dialog>

        <!-- 图片插入对话框 -->
        <el-dialog v-model="imageDialogVisible" title="插入图片" width="400px">
            <el-form :model="imageForm" label-width="80px">
                <el-form-item label="图片描述">
                    <el-input v-model="imageForm.alt" placeholder="请输入图片描述" />
                </el-form-item>
                <el-form-item label="图片地址">
                    <el-input v-model="imageForm.url" placeholder="请输入图片地址" />
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="imageDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="confirmInsertImage">确定</el-button>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
// ==================== 导入依赖 ====================

/**
 * Vue 3 组合式 API
 * @description 用于响应式数据管理和生命周期钩子
 */
import { ref, computed, watch, nextTick } from 'vue'

/**
 * Element Plus 组件和消息提示
 * @description 用于UI组件和用户反馈
 */
import { ElMessage } from 'element-plus'

/**
 * Element Plus 图标组件
 * @description 用于界面图标显示
 */
import {
    Edit,
    List,
    Check,
    Link,
    Picture,
    Grid,
    Minus,
    Upload,
    ArrowDown,
    Document,
    ChatDotRound,
    Close,
    More,
    View,
    DocumentCopy
} from '@element-plus/icons-vue'

/**
 * Marked 库用于 Markdown 解析
 * @description 用于将 Markdown 转换为 HTML
 */
import { marked } from 'marked'

/**
 * DOMPurify 用于 HTML 内容净化
 * @description 防止 XSS 攻击
 */
import DOMPurify from 'dompurify'

/**
 * 拖拽上传指令
 * @description 用于实现拖拽上传图片功能
 */
import dragUpload from '../directives/dragUpload'

// ==================== 组件属性定义 ====================

/**
 * 组件属性接口定义
 * @description 定义组件接收的属性类型
 */
interface Props {
    modelValue?: string // 双向绑定的内容值
    placeholder?: string // 占位符文本
    height?: string // 编辑器高度
    theme?: 'light' | 'dark' // 编辑器主题
}

/**
 * 组件属性默认值
 * @description 设置属性的默认值
 */
const props = withDefaults(defineProps<Props>(), {
    modelValue: '',
    placeholder: '请输入 Markdown 内容...',
    height: '400px',
    theme: 'light'
})

/**
 * 组件事件定义
 * @description 定义组件触发的事件
 */
const emit = defineEmits<{
    'update:modelValue': [value: string] // 内容更新事件
    'change': [value: string] // 内容变化事件
    'file-upload': [file: File, title: string] // 文件上传事件，包含文件名作为标题
}>()

// ==================== 响应式数据定义 ====================

/**
 * 编辑器内容
 * @description 存储 Markdown 文本内容
 */
const content = ref(props.modelValue)

/**
 * 编辑器模式
 * @description 控制编辑器的显示模式：编辑、预览、分屏
 */
const editorMode = ref<'edit' | 'preview' | 'split'>('edit')

/**
 * 链接对话框显示状态
 * @description 控制链接插入对话框的显示
 */
const linkDialogVisible = ref(false)

/**
 * 图片对话框显示状态
 * @description 控制图片插入对话框的显示
 */
const imageDialogVisible = ref(false)

/**
 * 链接表单数据
 * @description 存储链接插入的表单数据
 */
const linkForm = ref({
    text: '',
    url: ''
})

/**
 * 图片表单数据
 * @description 存储图片插入的表单数据
 */
const imageForm = ref({
    alt: '',
    url: ''
})

/**
 * 文件上传组件引用
 * @description 用于访问文件上传组件的方法
 */
const fileUploadRef = ref()

/**
 * 处理图片拖拽上传
 * @description 处理拖拽上传的图片文件，先尝试后端API，失败时使用本地Blob URL
 * @param files - 拖拽的文件列表
 */
const handleImageDrop = async (files: FileList) => {
  for (const file of Array.from(files)) {
    // 验证文件类型
    if (!file.type.startsWith('image/')) {
      ElMessage.warning(`文件 ${file.name} 不是图片格式`)
      continue
    }

    // 验证文件大小（限制为5MB）
    if (file.size > 5 * 1024 * 1024) {
      ElMessage.warning(`文件 ${file.name} 超过5MB限制`)
      continue
    }

    // 显示上传进度
    ElMessage.info(`图片 ${file.name} 上传中...`)

    try {
      // 首先尝试后端API上传
      const formData = new FormData()
      formData.append('image', file)
      
      const response = await fetch('/api/upload/image', {
        method: 'POST',
        body: formData
      })
      
      if (response.ok) {
        const result = await response.json()
        if (result.success && result.url) {
          // 后端上传成功，使用服务器返回的URL
          const imageText = `![${file.name}](${result.url})`
          insertMarkdown(imageText, '')
          ElMessage.success(`图片 ${file.name} 上传成功`)
          continue
        } else {
          throw new Error(result.message || '服务器返回错误')
        }
      } else {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }
    } catch (error: any) {
      // 后端API上传失败，回退到本地Blob URL
      console.warn(`图片 ${file.name} 后端API上传失败，使用本地Blob URL:`, error.message)
      
      ElMessage.warning(`图片 ${file.name} 服务器上传失败，已保存到本地预览`)
      
      // 创建本地Blob URL
      const localUrl = URL.createObjectURL(file)
      const imageText = `![${file.name}](${localUrl})`
      insertMarkdown(imageText, '')
      
      // 显示详细信息
      ElMessage({
        message: `图片 ${file.name} 已保存到本地预览，刷新页面后将丢失`,
        type: 'warning',
        duration: 5000
      })
    }
  }
}

/**
 * 拖拽上传选项
 * @description 传递给 v-drag-upload 指令的选项
 */
const dragUploadOptions = ref({
    accept: 'image/*',
    multiple: true,
    onDrop: handleImageDrop,
    onDragEnter: () => {
        ElMessage.info('拖拽图片到此处上传')
    },
    onDragLeave: () => {
        // 可以在这里添加拖拽离开的视觉反馈
    }
})

// ==================== 计算属性 ====================

/**
 * 渲染后的 HTML 内容
 * @description 将 Markdown 内容转换为安全的 HTML
 */
const renderedContent = computed(() => {
    if (!content.value) return ''

    try {
        // 转换 Markdown 为 HTML
        const html = marked.parse(content.value) as string

        // 使用 DOMPurify 净化 HTML
        return DOMPurify.sanitize(html, {
            ALLOWED_TAGS: [
                'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
                'p', 'br', 'hr',
                'strong', 'em', 'del', 'code', 'pre',
                'ul', 'ol', 'li',
                'blockquote',
                'a', 'img',
                'table', 'thead', 'tbody', 'tr', 'th', 'td',
                'div', 'span'
            ],
            ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'id']
        })
    } catch (error) {
        console.error('Markdown 解析错误:', error)
        return content.value
    }
})

// ==================== 方法定义 ====================

/**
 * 插入 Markdown 语法
 * @description 在选中的文本前后插入指定的 Markdown 标记
 * @param before - 插入到选中文本前的标记
 * @param after - 插入到选中文本后的标记
 */
const insertMarkdown = (before: string, after: string) => {
    const textarea = document.querySelector('.markdown-textarea textarea') as HTMLTextAreaElement
    if (!textarea) return

    const start = textarea.selectionStart
    const end = textarea.selectionEnd
    const selectedText = content.value.substring(start, end)

    // 构建新的文本内容
    const newText = content.value.substring(0, start) +
        before + selectedText + after +
        content.value.substring(end)

    content.value = newText
    emit('update:modelValue', newText)

    // 设置光标位置到选中文本的中间
    nextTick(() => {
        textarea.focus()
        textarea.setSelectionRange(start + before.length, start + before.length + selectedText.length)
    })
}

/**
 * 插入标题
 * @description 根据标题级别插入对应的 Markdown 标题语法
 * @param level - 标题级别 (h1-h6)
 */
const insertHeading = (level: string) => {
    const prefix = '#'.repeat(parseInt(level.slice(1))) + ' '
    insertMarkdown(prefix, '')
}

/**
 * 插入链接
 * @description 打开链接插入对话框
 */
const insertLink = () => {
    const textarea = document.querySelector('.markdown-textarea textarea') as HTMLTextAreaElement
    if (!textarea) return

    const start = textarea.selectionStart
    const end = textarea.selectionEnd
    const selectedText = content.value.substring(start, end)

    linkForm.value.text = selectedText
    linkForm.value.url = ''
    linkDialogVisible.value = true
}

/**
 * 确认插入链接
 * @description 在对话框中确认插入链接
 */
const confirmInsertLink = () => {
    if (!linkForm.value.text || !linkForm.value.url) {
        ElMessage.warning('请填写完整的链接信息')
        return
    }

    const linkText = `[${linkForm.value.text}](${linkForm.value.url})`
    insertMarkdown(linkText, '')
    linkDialogVisible.value = false
}

/**
 * 插入图片
 * @description 打开图片插入对话框
 */
const insertImage = () => {
    imageForm.value.alt = ''
    imageForm.value.url = ''
    imageDialogVisible.value = true
}

/**
 * 确认插入图片
 * @description 在对话框中确认插入图片
 */
const confirmInsertImage = () => {
    if (!imageForm.value.url) {
        ElMessage.warning('请填写图片地址')
        return
    }

    const imageText = `![${imageForm.value.alt}](${imageForm.value.url})`
    insertMarkdown(imageText, '')
    imageDialogVisible.value = false
}

/**
 * 插入代码块
 * @description 插入多行代码块
 */
const insertCodeBlock = () => {
    const textarea = document.querySelector('.markdown-textarea textarea') as HTMLTextAreaElement
    if (!textarea) return

    const start = textarea.selectionStart
    const selectedText = content.value.substring(start, textarea.selectionEnd)

    const codeBlock = selectedText ? `\`\`\`\n${selectedText}\n\`\`\`` : '\n```\n\n```\n'
    const newText = content.value.substring(0, start) + codeBlock + content.value.substring(textarea.selectionEnd)

    content.value = newText
    emit('update:modelValue', newText)

    nextTick(() => {
        textarea.focus()
        if (selectedText) {
            textarea.setSelectionRange(start + 4, start + 4 + selectedText.length)
        } else {
            textarea.setSelectionRange(start + 5, start + 5)
        }
    })
}

/**
 * 插入表格
 * @description 插入 Markdown 表格模板
 */
const insertTable = () => {
    const tableTemplate = `\n| 列1 | 列2 | 列3 |\n| --- | --- | --- |\n| 内容1 | 内容2 | 内容3 |\n`
    insertMarkdown(tableTemplate, '')
}

/**
 * 文件上传前验证
 * @description 验证上传文件的类型和大小
 * @param file - 上传的文件对象
 * @returns 是否允许上传
 */
const beforeFileUpload = (file: File) => {
    const isMarkdown = file.type === 'text/markdown' ||
        file.name.endsWith('.md') ||
        file.name.endsWith('.markdown')
    const isLt10M = file.size / 1024 / 1024 < 10

    if (!isMarkdown) {
        ElMessage.error('只能上传 Markdown 文件!')
        return false
    }
    if (!isLt10M) {
        ElMessage.error('文件大小不能超过 10MB!')
        return false
    }

    return true
}

/**
 * 文件上传成功回调
 * @description 处理文件上传成功后的内容解析
 * @param response - 服务器响应数据
 */
const handleFileSuccess = (response: any) => {
    if (response.content) {
        content.value = response.content
        emit('update:modelValue', response.content)
        ElMessage.success('文件内容已加载')
    } else {
        ElMessage.error('文件内容解析失败')
    }
}

/**
 * 自定义文件上传处理
 * @description 处理文件上传，读取文件内容并加载到编辑器，同时提取文件名作为标题
 * @param options - 上传选项
 */
const handleFileUpload = (options: any) => {
    const { file } = options

    // 提取文件名（去掉后缀）作为标题
    const fileName = file.name
    const titleWithoutExtension = fileName.replace(/\.(md|markdown)$/i, '')

    // 创建 FileReader 来读取文件内容
    const reader = new FileReader()

    reader.onload = (e) => {
        try {
            const fileContent = e.target?.result as string
            if (fileContent) {
                // 更新编辑器内容
                content.value = fileContent
                emit('update:modelValue', fileContent)

                // 触发文件上传事件，传递文件名作为标题
                emit('file-upload', file, titleWithoutExtension)

                ElMessage.success(`文件"${titleWithoutExtension}"已加载`)

                // 调用成功回调
                options.onSuccess()
            } else {
                ElMessage.error('文件内容为空')
                options.onError()
            }
        } catch (error) {
            console.error('文件读取错误:', error)
            ElMessage.error('文件读取失败')
            options.onError()
        }
    }

    reader.onerror = () => {
        ElMessage.error('文件读取失败')
        options.onError()
    }

    // 开始读取文件
    reader.readAsText(file)
}

/**
 * 文件上传失败回调
 * @description 处理文件上传失败的情况
 */
const handleFileError = () => {
    ElMessage.error('文件上传失败')
}

/**
 * 内容变化处理
 * @description 处理编辑器内容变化事件
 */
const handleContentChange = () => {
    emit('update:modelValue', content.value)
    emit('change', content.value)
}

// ==================== 监听器 ====================

/**
 * 监听 modelValue 变化
 * @description 当外部传入的内容变化时，同步更新内部内容
 */
watch(() => props.modelValue, (newValue) => {
    if (newValue !== content.value) {
        content.value = newValue
    }
}, { immediate: true })

/**
 * 监听内容变化
 * @description 当内部内容变化时，同步更新外部绑定值
 */
watch(content, (newValue) => {
    emit('update:modelValue', newValue)
})

// ==================== 指令注册 ====================

/**
 * 注册拖拽上传指令
 * @description 使组件内部可以使用 v-drag-upload 指令
 */
const vDragUpload = dragUpload
</script>

<style scoped lang="scss">
// 导入 Markdown 编辑器样式
@use '../assets/styles/components/_markdown-editor';
</style>
